package module

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"unsafe"

	"gitee.com/rouchie/omg/tcpclient"

	"github.com/sirupsen/logrus"
)

type Register struct {
	Status string `json:"status"`
	Chn    uint16 `json:"chn"`
}

type GoModuleRouter struct {
	m       *GoModule
	b       []byte
	mapFunc map[uint32]func([]byte)
}

func NewGoModuleRouter() *GoModuleRouter {
	r := &GoModuleRouter{
		b:       []byte{},
		mapFunc: make(map[uint32]func([]byte)),
	}

	r.mapFunc[CMD_REQUEST_FAILED] = func(i []byte) {
		logrus.Infof("request failed: %v", string(i))
	}

	r.mapFunc[CMD_REGISTER] = func(i []byte) {
		logrus.Debugf("register respond: %v", string(i))

		info := Register{}
		json.Unmarshal(i, &info)
		logrus.Debugf("register: %v", info)

		r.m.moduleChn = info.Chn

		// 必须在register返回之后，才可以发送其他命令
		r.m.fconnection()
	}

	return r
}

func (r *GoModuleRouter) AddCmd(cmd uint32, f func([]byte)) {
	r.mapFunc[cmd] = f
}

func (r *GoModuleRouter) Read(i []byte) {
	// 不知道这样写会不会导致性能低下
	r.b = append(r.b, i...)

	for {
		msg := Msg_Info_St{}

		var sizeHeader int = int(unsafe.Sizeof(msg))
		if len(r.b) < sizeHeader {
			return
		}

		logrus.Debugf("sizeheader(%v) module read (%v) bytes (%v)", sizeHeader, len(r.b), r.b)

		readBuf := bytes.NewBuffer(r.b[:sizeHeader])
		binary.Read(readBuf, binary.LittleEndian, &msg)

		logrus.Debugf("magci: (%v) src(%v:%v) dst(%v:%v) type(%v) len(%v)", msg.MagicNum, msg.ModuleSrcId, msg.ModuleSrcChnId, msg.ModuleDevId, msg.ModuleDevChnId, msg.MsgType, msg.MsgLen)

		var s int = sizeHeader + int(msg.MsgLen)

		if len(r.b) < s {
			return
		}

		f, ok := r.mapFunc[msg.MsgType]
		if ok {
			f(r.b[sizeHeader:s])
		} else {
			logrus.Errorf("router is not handle cmd[%X]", msg.MsgType)
		}

		r.b = r.b[s:]
	}
}

func (r *GoModuleRouter) Connection() {
	logrus.Debugf("connection")
	r.m.SendMsg(MODULE_CORE, CMD_REGISTER, []byte{})
}

func (r *GoModuleRouter) Disconnection() {
	logrus.Debugf("disconnection")
}

// GoModule 模块
type GoModule struct {
	client      tcpclient.TcpClient
	srcId       uint16
	moduleChn   uint16
	router      *GoModuleRouter
	fconnection func()
}

func NewGoModule(ip string, id uint16) *GoModule {
	router := NewGoModuleRouter()

	m := &GoModule{
		client:    tcpclient.NewClient("gomodule", ip, 0x3c6a<<2, router),
		moduleChn: 0,
		srcId:     id,
	}

	m.router = router
	router.m = m

	return m
}

func (m *GoModule) Start(f func()) error {
	m.fconnection = f

	err := m.client.Start()
	if err != nil {
		logrus.Errorf("module start failed (%v)", err)
		return err
	}

	return nil
}

func (m *GoModule) Stop() {
	logrus.Debugf("module goto stop")
	m.client.Stop()
}

func (m *GoModule) AddCmd(cmd uint32, f func([]byte)) {
	m.router.AddCmd(cmd, f)
}

func (m *GoModule) SendMsg(mod uint16, cmd uint32, b []byte) {
	buf := &bytes.Buffer{}
	binary.Write(buf, binary.LittleEndian, Msg_Info_St{
		MagicNum:       MAGIC_NUM,
		ModuleSrcId:    m.srcId,
		ModuleSrcChnId: m.moduleChn,
		ModuleDevId:    mod,
		ModuleDevChnId: 0,
		MsgType:        cmd,
		MsgLen:         uint32(len(b)),
	})

	binary.Write(buf, binary.LittleEndian, b)
	m.client.Write(buf.Bytes())
}
